﻿using System.Linq;
using System.Web.Mvc;
using System.Collections.Generic;
using System;

namespace Nais.Presentation.Web.Mvc
{
    /// <summary>
    ///  Custom metadata provider. Enables us to inject our own conventions for custom mvc template binding.
    /// </summary>
    public class PluggableConventionsModelMetadataProvider : ModelMetadataProvider
    {
        private readonly ModelMetadataProvider _innerProvider;
        private readonly List<Action<WrappedModelMetadata>> _conventions = new List<Action<WrappedModelMetadata>>();
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="innerProvider"></param>
        public PluggableConventionsModelMetadataProvider(ModelMetadataProvider innerProvider)
        {
            _innerProvider = innerProvider;
        }

        /// <summary>
        /// Gets a <see cref="T:System.Web.Mvc.ModelMetadata"/> object for each property of a model.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.Web.Mvc.ModelMetadata"/> object for each property of a model.
        /// </returns>
        /// <param name="container">The container.</param><param name="containerType">The type of the container.</param>
        public override IEnumerable<ModelMetadata> GetMetadataForProperties(object container, Type containerType)
        {
            return _innerProvider.GetMetadataForProperties(container, containerType).Select(CreateMetadata);
        }

        /// <summary>
        /// Gets metadata for the specified property.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.Web.Mvc.ModelMetadata"/> object for the property.
        /// </returns>
        /// <param name="modelAccessor">The model accessor.</param><param name="containerType">The type of the container.</param><param name="propertyName">The property to get the metadata model for.</param>
        public override ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, string propertyName)
        {
            ModelMetadata innerMetadata = _innerProvider.GetMetadataForProperty(modelAccessor, containerType, propertyName);
            return CreateMetadata(innerMetadata);
        }

        /// <summary>
        /// Gets metadata for the specified model accessor and model type.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.Web.Mvc.ModelMetadata"/> object for the specified model accessor and model type.
        /// </returns>
        /// <param name="modelAccessor">The model accessor.</param><param name="modelType">The type of the model.</param>
        public override ModelMetadata GetMetadataForType(Func<object> modelAccessor, Type modelType)
        {
            ModelMetadata innerMetadata = _innerProvider.GetMetadataForType(modelAccessor, modelType);
            return CreateMetadata(innerMetadata);
        }

        /// <summary>
        /// Add a convention
        /// </summary>
        /// <param name="convention"></param>
        public void AddConvention(Action<WrappedModelMetadata> convention)
        {
            _conventions.Add(convention);
        }

        private ModelMetadata CreateMetadata(ModelMetadata innerMetadata)
        {
            WrappedModelMetadata metadata = new WrappedModelMetadata(this, innerMetadata);
            foreach (var convention in _conventions)
            {
                convention(metadata);
            }
            return metadata;
        }
    }
}